 package net.ruixin.orm.hibernate;

 import net.ruixin.utils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

 public class ComplexHibernateTemplate<T, PK extends Serializable>
   implements ISimpleHibernateTemplate<T, PK>
 {
   private Log logger = LogFactory.getLog(super.getClass());
   public SessionFactory sessionFactory;
   protected Session session;
   protected Class<T> entityClass;

   public ComplexHibernateTemplate()
   {
     this.entityClass = null;
     Class c = super.getClass();
     Type t = c.getGenericSuperclass();
     if (t instanceof ParameterizedType) {
       Type[] p = ((ParameterizedType)t).getActualTypeArguments();
       this.entityClass = ((Class)p[0]);
     }
   }

   public ComplexHibernateTemplate(SessionFactory sessionFactory, Class<T> entityClass) {
     this.sessionFactory = sessionFactory;
   }

   public Session getSession()
   {
     return this.sessionFactory.getCurrentSession();
   }

   public SessionFactory getSessionFactory()
   {
     return this.sessionFactory;
   }

   @Autowired
   public void setSessionFactory(@Qualifier("sessionFactory") SessionFactory sessionFactory) {
     this.sessionFactory = sessionFactory;
   }

   public void save(T entity) {
     Assert.notNull(entity);
     getSession().saveOrUpdate(entity);
     this.logger.info("save entity: {}" + entity);
   }

   public void delete(T entity) {
     Assert.notNull(entity);
     getSession().saveOrUpdate(entity);
     this.logger.info("delete entity: {}" + entity);
   }

   public void delete(PK id) {
     Assert.notNull(id);
     delete(get(id));
   }

   public List<T> findAll(OrderBy[] orders) {
     Criteria crit = createCriteria(new Criterion[0]);

     if (orders != null) {
       for (OrderBy order : orders) {
         crit.addOrder(order.getOrder());
       }
     }
     return crit.list();
   }

   public Page<T> findAll(Page<T> page) {
     return findByCriteria(page, new Criterion[0]);
   }

   public T get(PK id)
   {
     return (T)getSession().get(this.entityClass, id);
   }

   public List find(String hql, Object... values)
   {
     return createQuery(hql, values).list();
   }

   public Page<T> find(Page<T> page, String hql, Object... values)
   {
     Assert.notNull(page);

     if (page.isAutoCount()) {
       this.logger.warn("HQL查询暂不支持自动获取总结果数,hql为{}" + hql);
     }
     Query q = createQuery(hql, values);

     if (page.isFirstSetted()) {
       q.setFirstResult(page.getFirst());
     }
     if (page.isPageSizeSetted()) {
       q.setMaxResults(page.getPageSize());
     }
     page.setResult(q.list());
     return page;
   }

   public Object findUnique(String hql, Object... values)
   {
     return createQuery(hql, values).uniqueResult();
   }

   public Integer findInt(String hql, Object... values)
   {
     return (Integer)findUnique(hql, values);
   }

   public Long findLong(String hql, Object... values)
   {
     return (Long)findUnique(hql, values);
   }

   public List<T> findByCriteria(Criterion[] criterion)
   {
     return createCriteria(criterion).list();
   }

   public Page<T> findByCriteria(Page page, Criterion[] criterion)
   {
     Assert.notNull(page);

     Criteria c = createCriteria(criterion);

     if (page.isAutoCount()) {
       page.setTotalCount(countQueryResult(page, c));
     }
     if (page.isFirstSetted()) {
       c.setFirstResult(page.getFirst());
     }
     if (page.isPageSizeSetted()) {
       c.setMaxResults(page.getPageSize());
     }

     if (page.isOrderBySetted()) {
       if (page.getOrder().endsWith("asc"))
         c.addOrder(Order.asc(page.getOrderBy()));
       else {
         c.addOrder(Order.desc(page.getOrderBy()));
       }
     }
     page.setResult(c.list());
     return page;
   }

   public List<T> findByProperty(String propertyName, Object value, OrderBy[] orders)
   {
     Assert.hasText(propertyName);
     Criteria crit = createCriteria(new Criterion[] { Restrictions.eq(propertyName, value) });

     if (orders != null) {
       for (OrderBy order : orders) {
         crit.addOrder(order.getOrder());
       }
     }

     return crit.list();
   }

   public T findUniqueByProperty(String propertyName, Object value)
   {
     Assert.hasText(propertyName);
     return (T)createCriteria(new Criterion[] { Restrictions.eq(propertyName, value) }).uniqueResult();
   }

   public Query createQuery(String queryString, Object... values)
   {
     Assert.hasText(queryString);
     Query queryObject = getSession().createQuery(queryString);
     if (values != null) {
       for (int i = 0; i < values.length; ++i) {
         queryObject.setParameter(i, values[i]);
       }
     }
     return queryObject;
   }

   public Criteria createCriteria(Criterion[] criterions)
   {
     Criteria criteria = getSession().createCriteria(this.entityClass);
     for (Criterion c : criterions) {
       criteria.add(c);
     }
     return criteria;
   }

   public boolean isPropertyUnique(String propertyName, Object newValue, Object orgValue)
   {
     if ((newValue == null) || (newValue.equals(orgValue))) {
       return true;
     }
     Object object = findUniqueByProperty(propertyName, newValue);
     return object == null;
   }

   protected int countQueryResult(Page<T> page, Criteria c)
   {
     CriteriaImpl impl = (CriteriaImpl)c;

     Projection projection = impl.getProjection();
     ResultTransformer transformer = impl.getResultTransformer();

     List orderEntries = null;
     try {
       orderEntries = (List)BeanUtils.getFieldValue(impl, "orderEntries");
       BeanUtils.setFieldValue(impl, "orderEntries", new ArrayList());
     } catch (Exception e) {
       this.logger.error("不可能抛出的异常:{}", e);
     }

     int totalCount = ((Long)c.setProjection(Projections.rowCount()).uniqueResult()).intValue();
     if (totalCount < 1) {
       return -1;
     }

     c.setProjection(projection);

     if (projection == null) {
       c.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
     }
     if (transformer != null) {
       c.setResultTransformer(transformer);
     }
     try
     {
       BeanUtils.setFieldValue(impl, "orderEntries", orderEntries);
     } catch (Exception e) {
       this.logger.error("不可能抛出的异常:{}", e);
     }

     return totalCount;
   }

   public SQLQuery createSQLQuery(String queryString, Object... values) {
     Assert.hasText(queryString);
     SQLQuery queryObject = getSession().createSQLQuery(queryString);
     if (values != null) {
       for (int i = 0; i < values.length; ++i) {
         queryObject.setParameter(i, values[i]);
       }
     }
     return queryObject;
   }

   public List findBySQL(String sql, Object... values) {
     return createSQLQuery(sql, values).list();
   }

   public void executeSQL(String sql, Object... values) {
     createSQLQuery(sql, values).executeUpdate();
   }
 }

